package ccpvspiral1;

import java.util.ArrayList;

import population.Population;
import population.PopulationInterface;

/**
 * Contains settings that will be used during the runtime. For example this will contain information on how trends and parameters will change over time.
 * This will also encapsulate the hierarchical labels used for output.
 * @author Robert Puckett
 *
 */
public class RunTimeSettings {
	public ArrayList<NamedArrayList<String>> characteristics = new ArrayList<NamedArrayList<String>>();
	public ArrayList<String> geolocations = new ArrayList<String>();
	private ArrayList<Epoch> epochs = new ArrayList<Epoch>();
	private PopulationInterface pop;
	private int firstYear = 0;
	
	
	
	
	public RunTimeSettings() {
		
	}
	
	public Double getAltruism(int[] source, int[] target, int date) {
		return getEpoch(source).getAltruism(getEpochIndex(target), date).getValueAt(0);
	}

	public void addAltruism(int[] source, int[] target, int date, double value) {
		getEpoch(source).addAltruism(getEpochIndex(target), date,value);
	}

	public void updateAltruism(int date, int[] source, int[] target, double value) {
		getEpoch(source).addAltruism(getEpochIndex(target), date,value);
	}
	
	/**
	 * 
	 * @param groupIndex
	 * @param year
	 * @return
	 */
	public int getPopulation(int groupIndex, int year) {
		// THIS PROBABLY SHOULDN'T BE USING GETQTYAT! Doesn't have linear component
		return (int)epochs.get(groupIndex).getLinearPopulation(firstYear, year);
	}
	
	public int getNumberEpochs() {
		return epochs.size();
	}
	
	public int getGeoLocation(int groupIndex) {
		return epochs.get(groupIndex).getGeolocation();
	}
	
	public int[] getAscriptiveArray(int groupIndex) {
		ArrayList<Integer> arr = epochs.get(groupIndex).getCharacteristics();
		int[] results = new int[arr.size()];
		for(int i = 0; i< arr.size(); i++) {
			results[i] = arr.get(i);
		}
		return results;
	}
	
	
	/**
	 * Returns the number of ascriptive characteristics in the i^th group of ascriptive characteristics. 
	 * @param i
	 * @return
	 */
	public int getNumChar(int i) {
		return characteristics.get(i).size();
	}
	
  /**
   * Returns the number of ascriptive characteristics in all groups of ascriptive characteristics.
   * @return
   */
  public int[] getNumChar() {
	   int[] results = new int[characteristics.size()];
	  for (int i = 0; i<characteristics.size(); i++) {
  		  results[i] = characteristics.get(i).size();
	  }
	  return results;	
  }
	
	

	/**
	 * Initializes runtimesettings with a list of ascriptive characteristic category names.
	 * @param names
	 */
	public RunTimeSettings(ArrayList<String> names) {
		for(String st: names) {
			characteristics.add(new NamedArrayList<String>(st));
		}
	}
	
	public void  InitRunTimeSettings(ArrayList<String> names) {
		for(String st: names) {
			characteristics.add(new NamedArrayList<String>(st));
		}
	} 
	
	/**
	 * Initializes runtimesettings with an array of ascriptive characteristic category names.
	 * @param names
	 */
	public RunTimeSettings(String[] names) {
		for(String st: names) {
			characteristics.add(new NamedArrayList<String>(st));
		}
	}
	
	private ArrayList<Epoch> getEpochs() {
		return epochs;
	}
	
	private void addCategory(String name) {
		this.characteristics.add(new NamedArrayList<String>(name));
	}
	
	public void addToCharacteristics(int index, String name) {
		this.characteristics.get(index).add(name);
	}
	
	
	/**
	 * Returns the index within the category for the specified string. If the string does not exist in that category, then it is added, and the new index is returned.
	 * @param category
	 * @param value
	 * @return
	 */
	public int getIndex(int category, String value) {
		//ensure arraylist exists
		if (category > characteristics.size()-1) {
			// prevent array index error by adding an arraylist if none exists
			//System.out.println("Add new category");
			for (int i = characteristics.size(); i<=category; i++) {
				characteristics.add(new NamedArrayList<String>());
			}
		}
		
		NamedArrayList<String> values = characteristics.get(category);
		for (int i = 0; i<values.size(); i++) {
		//	System.out.println("Does '"+values.get(i)+"' == '"+value+"' ?");
			if (values.get(i).equals(value)) {
		//		System.out.println("Yes at " + i);
				return i;
			}
		}
		//System.out.println("No adding it at " + this.characteristics.get(category).size());
		// entry wasn't found, add it;
		this.characteristics.get(category).add(value);
		return this.characteristics.get(category).size()-1;
		
	}
	
	public int getLocationIndex(String name) {
			for (int i = 0; i<geolocations.size(); i++) {
				if (geolocations.get(i).equals(name)) {
					return i;
				}
			}
			System.out.println("New location: " + name);
			this.geolocations.add(name);
			return this.geolocations.size()-1;
	}
	
	/**
	 * Converts a list of string values to their corresponding integer representations;
	 * @param values
	 * @return
	 */
	public ArrayList<Integer> getIndexes(ArrayList<String> values) {
		int size = values.size();
		ArrayList<Integer> arrint = new ArrayList<Integer>();
		
		for (int i = 0; i<size; i++) {
			arrint.add(getIndex(i,values.get(i)));
		}
		return arrint;
	}
	
	public String getString(int category, int index) {
		return characteristics.get(category).get(index);
	}
	
	public ArrayList<String> getStrings(ArrayList<Integer> values) {
		int size = values.size();
		ArrayList<String> arrst = new ArrayList<String>();
		for (int i = 0; i<size; i++) {
			arrst.add(getString(i,values.get(i)));
		}
		return arrst;
	}
	
	public void setCharacteristicsNames(ArrayList<String> names) {
		assert this.characteristics.size() == names.size();
		for (int i=0; i<this.characteristics.size();i++) {
			this.characteristics.get(i).name = names.get(i);
		}
		
	}
	
	private Epoch getEpoch(int i) {
		return epochs.get(i);
	}
	
	
	public void addDatedValue(String group, int location, ArrayList<Integer> arr2, DatedValues<Integer> dv) {
		boolean isFound = false;
		ArrayList<Integer> arr1 = new ArrayList<Integer>();
		for (Epoch e: epochs) {
			isFound = false;
			if (e.getName().equals(group)) { // must have same name
				if (e.getGeolocation() == location) {	// must be in same location
				
				//System.out.println("  Group Name Found");
			arr1 = e.getCharacteristics();
/*				for(int i: arr1) {
					System.out.print(" " + i);
				}
				System.out.print(" == ");
				for(int i: arr2) {
					System.out.print(" " + i);
				}
				System.out.println(" ?");
				*/
				isFound = true;
				// is it the same group? check the characteristics
				for (int i=0; i<arr1.size(); i++) {
				//	System.out.println(arr1.get(i)+"=="+arr2.get(i)+"?");
					if (arr1.get(i)!=arr2.get(i)) {
				//		System.out.println("  Same Name, Different Group");
						isFound = false;
					}
				}
				if (isFound) {
					e.addDatedValue(dv); // add the value to this group, it matches
					return;
				}
			}
			}

		}
		if (!isFound) { // either no epochs, no name matches, or a name matches but characteristics didn't
			//System.out.println("  Unique new group found.");
			epochs.add(new Epoch(group,location,arr2,dv));// String, ArrayList<Integer>, DatedValues
		}
		
	}
	
	public void clearCharacteristics() {
		characteristics.clear();
	}
	
	private void clearEpochs() {
		epochs.clear();
	}
	
	public void clear() {
		characteristics.clear();
		epochs.clear();
	}
		
	
	public String toString() {
		String st = "RunTimeSettings: \n";
		st = st + "Labels:\n";
		for (NamedArrayList<String> value: characteristics) {
		  st = st + " " + value.toString() + "\n";	
		}
		st = st + "Geo-Locations\n";
		for (String value: geolocations) {
			  st = st + " " + value + "\n";	
			}
		st = st + "Epochs:\n";
		for (Epoch value: epochs) {
			  st = st + " " + value.toString() + "\n";	
			}
		return st;
	}
	/*
List of:  (to store labels)
    NamedList<String>

List of: (to store combinations)
    String name
    List of DatedValue<Integer>
    List of Integers (representing parameters)
    */

	/**
	 * @param firstYear the firstYear to set
	 */
	public void setFirstYear(int firstYear) {
		this.firstYear = firstYear;
	}

	/**
	 * @return the firstYear
	 */
	public int getFirstYear() {
		return firstYear;
	}

	/**
	 * Finds and returns the epoch with the matching ascriptive character.
	 * @param character
	 * @return
	 */
	private int getEpochIndex(int[] character) {
		boolean match = true;
		ArrayList<Integer> adata;
		for (int index = 0; index < epochs.size(); index++)  {
			match = true;
			adata = epochs.get(index).getCharacteristics();
			for (int i = 0; i< character.length; i++) {
				if (adata.get(i) != character[i]) {
					match = false;
					continue;
				}
			}
			if (match) {
				return index;
			}
		}
		return -1;
	}
	
	/**
	 * Finds and returns the epoch with the matching ascriptive character.
	 * @param character
	 * @return
	 */
	private Epoch getEpoch(int[] character) {
		boolean match = true;
		ArrayList<Integer> adata;
		for (int index = 0; index < epochs.size(); index++)  {
			match = true;
			adata = epochs.get(index).getCharacteristics();
			for (int i = 0; i< character.length; i++) {
				if (adata.get(i) != character[i]) {
					match = false;
					continue;
				}
			}
			if (match) {
				return epochs.get(index);
			}
		}
		return null;
	}

	/**
	 * Adds the altruism value to the data structure. 
	 * @param chars1
	 * @param chars2
	 * @param year
	 * @param value
	 */
	public void addAltruism(ArrayList<Integer> chars1, ArrayList<Integer> chars2, int year, double value) {
		getEpoch(chars1).addAltruism(getEpochIndex(chars2), year, value);
	}

	/**
	 * Finds and returns the epoch with the matching ascriptive character.
	 * @param character
	 * @return
	 */
	private Epoch getEpoch(ArrayList<Integer> character) {
		boolean match = true;
		ArrayList<Integer> adata;
		for (int index = 0; index < epochs.size(); index++)  {
			match = true;
			adata = epochs.get(index).getCharacteristics();
			for (int i = 0; i< character.size(); i++) {
				if (adata.get(i) != character.get(i)) {
					match = false;
					continue;
				}
			}
			if (match) {
				return epochs.get(index);
			}
		}
		return null;
	}

	/**
	 * Finds and returns the index to the epoch with the matching ascriptive character.
	 * @param character
	 * @return
	 */
	private int getEpochIndex(ArrayList<Integer> character) {
		boolean match = true;
		ArrayList<Integer> adata;
		for (int index = 0; index < epochs.size(); index++)  {
			match = true;
			adata = epochs.get(index).getCharacteristics();
			for (int i = 0; i< character.size(); i++) {
				if (adata.get(i) != character.get(i)) {
					match = false;
					continue;
				}
			}
			if (match) {
				return index;
			}
		}
		return -1;
	}

	/**
	 * Finds and returns the index to the epoch with the matching name in string. 
	 * @param string
	 * @return
	 */
	private int getEpochIndex(String string) {
		for (int i = 0; i < epochs.size(); i++)  {
			System.out.println("Founding:" + epochs.get(i).getName() + " =? " + string);
			if (epochs.get(i).getName().equals(string)) {
				System.out.println("Found:" + string);
				return i;
			}
		}
		return -1;
	}
	/**
	 * Finds and return the epoch with the matching name in string.
	 * @param string
	 * @return
	 */
	private Epoch getEpoch(String string) {
		for (int i = 0; i < epochs.size(); i++)  {
			if (epochs.get(i).getName().equals(string)) {
				System.out.println("Found:" + string);
				return epochs.get(i);
			}
		}
		return null;
	}

	public void addAltruism(String name1, String name2, int year,
			double value) {
		if (getEpoch(name1) == null) System.out.println("null alert!");
		getEpoch(name1).addAltruism(getEpochIndex(name2), year,value);
	}
	
	public double getAltruism(String name1, String name2, int year,
			double value) {
		return getEpoch(name1).getAltruism(getEpochIndex(name2), year).getValueAt(0);
	}
	
	public int[] getAscriptiveLengths() {
		int[] results = new int[characteristics.size()];
		
		for(int i= 0; i<characteristics.size(); i++ ) {
			results[i] = characteristics.get(i).size();
		}
		return results;
	}

	/**
	 * @param pop the pop to set
	 */
	public void setPopulationInt(PopulationInterface pop) {
		this.pop = pop;
	}

	/**
	 * @return the pop
	 */
	public PopulationInterface getPopulationInt() {
		return pop;
	}
	
	public int getAscriptiveIndex(String st) {
		String val = st.trim();
		for (int i = 0; i<characteristics.size(); i++) {
		  if (characteristics.get(i).name == val) {
			  return i;
		  }
		}
		return -1; // not found
	}
	

	
}